import os
from random import randint

from django.contrib.auth import login, authenticate, logout
from django.db.models import Q
from django.http import HttpResponseBadRequest, HttpResponse, JsonResponse


# Create your views here.
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views import View

from home.models import ArticleCategory, Article
from libs.captcha.captcha import captcha
from django_redis import get_redis_connection

from users.models import User
from utils.response_code import RETCODE


class RegisterView(View):

    def get(self,request):
        return render(request,'register.html')

    def post(self,request):
        # 接收参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode = request.POST.get('sms_code')
        # 判断参数是否齐全
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('缺少必传参数')
        # 判断两次密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次密码不一致')
        # 校验短信验证码
        redis_conn = get_redis_connection('default')
        redis_sms_code = redis_conn.get("sms_register:%s" % mobile).decode("utf-8")
        if redis_sms_code is None:
            return HttpResponseBadRequest('验证码已过期')
        if redis_sms_code != smscode:
            return HttpResponseBadRequest('验证码错误')

        try:
            user = User.objects.create_user(username=mobile,mobile=mobile, password=password)

            # 我们可以借助django的login函数来完成身份的记录
            login(request, user=user)
        except:
            return HttpResponseBadRequest('注册失败')


        response = redirect(reverse('home:index'))

        # 设置cookie
        # 登录状态，会话结束后自动过期
        response.set_cookie('is_login', True)
        # 设置用户名有效期一个月
        response.set_cookie('username', user.username, max_age=30 * 24 * 3600)

        return response



class ImageCodeView(View):

    def get(self,request):
        uuid = request.GET.get('uuid')
        if uuid is None:
            return HttpResponseBadRequest('请求参数错误')
        text,image = captcha.generate_captcha()
        redis_conn = get_redis_connection('default')
        redis_conn.setex("img:%s" % uuid,300,text)
        # 返回响应，将生成的图片以content_type为image/jpeg的形式返回给请求
        return HttpResponse(image, content_type='image/jpeg')


    def post(self,request):
        pass


class SmsCodeView(View):

    def get(self,request):
        # 校验UUID
        uuid = request.GET.get('uuid')
        if uuid is None:
            return HttpResponseBadRequest('请求参数错误')
        # 校验验证码
        redis_conn = get_redis_connection('default')
        img_code = redis_conn.get("img:%s" % uuid).decode("utf-8") # b'Q5N8'
        if img_code is None:
            return HttpResponseBadRequest('验证码过期')
        req_image_code = request.GET.get('image_code')
        if img_code.lower() != req_image_code.lower():
            return HttpResponseBadRequest('验证码错误')
        # 校验手机号
        phone = request.GET.get("mobile")
        # -*- coding: utf-8 -*-
        from tencentcloud.common import credential
        from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
        # 导入对应产品模块的client models。
        from tencentcloud.sms.v20210111 import sms_client, models

        # 导入可选配置类
        from tencentcloud.common.profile.client_profile import ClientProfile
        from tencentcloud.common.profile.http_profile import HttpProfile
        try:
            # 实例化一个认证对象，入参需要传入腾讯云账户secretId，secretKey。
            # 为了保护密钥安全，建议将密钥设置在环境变量中或者配置文件中。
            # 硬编码密钥到代码中有可能随代码泄露而暴露，有安全隐患，并不推荐。
            # SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi
            cred = credential.Credential("AKIDMXWHo7C2e4II4e2C8qJYZNxJQL3Ypnri", "mMjsoW0HJAwZ1EDzYsyqW5SefU9dx61l")
            # cred = credential.Credential(
            #     os.environ.get("AKIDMXWHo7C2e4II4e2C8qJYZNxJQL3Ypnri"),
            #     os.environ.get("mMjsoW0HJAwZ1EDzYsyqW5SefU9dx61l"))

            # 实例化一个http选项，可选的，没有特殊需求可以跳过。
            httpProfile = HttpProfile()
            # 如果需要指定proxy访问接口，可以按照如下方式初始化hp（无需要直接忽略）
            # httpProfile = HttpProfile(proxy="http://用户名:密码@代理IP:代理端口")
            httpProfile.reqMethod = "POST"  # post请求(默认为post请求)
            httpProfile.reqTimeout = 10  # 请求超时时间，单位为秒(默认60秒)

            httpProfile.endpoint = "sms.tencentcloudapi.com"  # 指定接入地域域名(默认就近接入/)

            # 非必要步骤:
            # 实例化一个客户端配置对象，可以指定超时时间等配置
            clientProfile = ClientProfile()
            clientProfile.signMethod = "TC3-HMAC-SHA256"  # 指定签名算法
            clientProfile.language = "en-US"
            clientProfile.httpProfile = httpProfile

            # 实例化要请求产品(以sms为例)的client对象
            # 第二个参数是地域信息，可以直接填写字符串ap-guangzhou，支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8
            client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile)

            # 实例化一个请求对象，根据调用的接口和实际情况，可以进一步设置请求参数
            # 您可以直接查询SDK源码确定SendSmsRequest有哪些属性可以设置
            # 属性可能是基本类型，也可能引用了另一个数据结构
            # 推荐使用IDE进行开发，可以方便地跳转查阅各个接口和数据结构的文档说明
            req = models.SendSmsRequest()

            # 基本类型的设置:
            # SDK采用的是指针风格指定参数，即使对于基本类型您也需要用指针来对参数赋值。
            # SDK提供对基本类型的指针引用封装函数
            # 帮助链接：
            # 短信控制台: https://console.cloud.tencent.com/smsv2
            # 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81

            # 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId，示例如1400006666
            # 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
            req.SmsSdkAppId = "1400474878"
            # 短信签名内容: 使用 UTF-8 编码，必须填写已审核通过的签名
            # 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
            req.SignName = "琪沙拉"
            # 模板 ID: 必须填写已审核通过的模板 ID
            # 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
            req.TemplateId = "2364479"
            # 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致，，若无模板参数，则设置为空
            sms_code = '%04d' % randint(0, 999999)
            print("本次注册的验证码未:",sms_code)
            req.TemplateParamSet = [sms_code]

            redis_conn.setex("sms_register:%s" % phone, 1800, sms_code)
            # 下发手机号码，采用 E.164 标准，+[国家或地区码][手机号]
            # 示例如：+8613711112222， 其中前面有一个+号 ，86为国家码，13711112222为手机号，最多不要超过200个手机号
            req.PhoneNumberSet = ["+86" + phone]
            # 用户的 session 内容（无需要可忽略）: 可以携带用户侧 ID 等上下文信息，server 会原样返回
            req.SessionContext = ""
            # 短信码号扩展号（无需要可忽略）: 默认未开通，如需开通请联系 [腾讯云短信小助手]
            req.ExtendCode = ""
            # 国内短信无需填写该项；国际/港澳台短信已申请独立 SenderId 需要填写该字段，默认使用公共 SenderId，无需填写该字段。注：月度使用量达到指定量级可申请独立 SenderId 使用，详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。
            req.SenderId = ""

            resp = client.SendSms(req)

            # 输出json格式的字符串回包
            print(resp.to_json_string(indent=2))

            # 当出现以下错误码时，快速解决方案参考
            # - [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
            # - [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
            # - [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
            # - [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
            # - 更多错误，可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)

        except TencentCloudSDKException as err:
            print(err)
        # 响应结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})


class LoginView(View):
    def get(self,request):
        return render(request, 'login.html')

    def post(self,request):
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        remember = request.POST.get('remember')
        # 判断参数是否缺省
        if not all([mobile,password]):
            return JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '缺少必传参数'})

        # django本身就自带认证逻辑 完成登录？
        user = authenticate(mobile=mobile,password=password)
        # user = User.objects.filter(Q(mobile=mobile) & Q(password=password))
        if user is None:
            return HttpResponseBadRequest('用户名或密码错误')
        # 实现登录状态保存
        # 就是服务器对当前登录的用户做一份记录，记录的实现方案是基于session
        login(request,user)

        # 登录成功则跳转到首页
        response = redirect(reverse('home:index'))

        # 设置身份信息存活的生命周期
        if remember != "no":
            # 表示用户没有选择记住他
            request.session.set_expiry(0)
            # 设置cookie存储用户信息
            response.set_cookie("is_login",True)
            response.set_cookie("username",user.username,max_age=3600*24*15)
        else:
            # 表示用户选择记住他
            # None表示两周后过期
            request.session.set_expiry(None)
            # 设置cookie存储用户信息
            response.set_cookie("is_login", True,max_age=3600 * 24 * 15)
            response.set_cookie("username", user.username, max_age=3600 * 24 * 15)
        return response


class LogoutView(View):
    def get(self,request):
        logout(request)
        response = redirect(reverse('home:index'))
        response.delete_cookie('is_login')
        response.delete_cookie('username')
        return response

class ForgetPasswordView(View):

    def get(self, request):
        return render(request, 'forget_password.html')

    def post(self,request):
        # 接收参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode = request.POST.get('sms_code')

        # 判断参数是否齐全
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('缺少必传参数')


        # 判断两次密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次输入的密码不一致')

        # 验证短信验证码
        redis_conn = get_redis_connection('default')
        sms_code_server = redis_conn.get('sms_register:%s' % mobile)
        if sms_code_server is None:
            return HttpResponseBadRequest('短信验证码已过期')
        if smscode != sms_code_server.decode():
            return HttpResponseBadRequest('短信验证码错误')

        # 根据手机号查询数据
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # 如果该手机号不存在，则注册个新用户
            try:
                User.objects.create_user(username=mobile, mobile=mobile, password=password)
            except Exception:
                return HttpResponseBadRequest('修改失败，请稍后再试')
        else:
            # 修改用户密码
            user.set_password(password)
            user.save()

        # 跳转到登录页面
        response = redirect(reverse('users:login'))

        return response


class CenterUserView(View):
    def get(self,request):

        # 用户名
        # 头像
        # 电话
        # 简介
        if request.user.is_authenticated:
            user = request.user
            context = {
                "username":user.username,
                "mobile":user.mobile,
                "user_desc":user.user_desc,
                "avatar":user.avatar if user.avatar else None
            }
            return render(request, 'center.html',context=context)
        else:
            return redirect(reverse('users:login'))

    def post(self,request):
        # 接收数据
        user = request.user
        avatar = request.FILES.get('avatar')
        username = request.POST.get('username', user.username)
        user_desc = request.POST.get('desc', user.user_desc)
        # 修改数据库数据
        try:
            user.username = username
            user.user_desc = user_desc
            if avatar:
                user.avatar = avatar
            user.save()
        except Exception as e:
            return HttpResponseBadRequest('更新失败，请稍后再试')

        # 返回响应，刷新页面
        response = redirect(reverse('users:center'))
        # 更新cookie信息
        response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
        return response


class WriteBlogView(View):
    def get(self,request):
        articleCategorys = ArticleCategory.objects.all()
        context = {
            "categorys":articleCategorys
        }
        return render(request, 'write_blog.html',context=context)

    def post(self, request):
        # TODO 此处需要完善提交文章的代码
        # 接收数据
        avatar = request.FILES.get('avatar')
        title = request.POST.get('title')
        category_id = request.POST.get('category')
        tags = request.POST.get('tags')
        sumary = request.POST.get('sumary')
        content = request.POST.get('content')
        user = request.user

        # 验证数据是否齐全
        if not all([avatar, title, category_id, sumary, content]):
            return HttpResponseBadRequest('参数不全')

        # 判断文章分类id数据是否正确
        try:
            article_category = ArticleCategory.objects.get(id=category_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseBadRequest('没有此分类信息')

        # 保存到数据库
        try:
            article = Article.objects.create(
                author=user,
                avatar=avatar,
                category=article_category,
                tags=tags,
                title=title,
                sumary=sumary,
                content=content
            )
        except Exception as e:
            return HttpResponseBadRequest('发布失败，请稍后再试')

        # 返回响应，跳转到文章详情页面
        # 暂时先跳转到首页
        return redirect(reverse('home:index'))